package org.meluk.billboard.game.seaBattle;

import java.util.ArrayList;
import java.util.List;
import org.meluk.billboard.jpa.seabattle.SBBoard;
import org.meluk.billboard.jpa.seabattle.SBPoint;
import org.meluk.billboard.jpa.seabattle.Ship;
import org.meluk.billboard.util.RandomUtils;

public class SBUtil {

    public static boolean locateShip(SBBoard board, Ship ship, boolean checkPossibility) {
        if (checkPossibility && !checkLocationPossibility(board, ship)) {
            return false;
        }
        locateShipOnTheBoard(board, ship);
        return true;
    }

    public static boolean checkLocationPossibility(SBBoard board, Ship ship) {
        for (SBPoint point : ship.getPointList()) {
            if (!checkPointPossibility(board, point)) {
                return false;
            }
        }
        return true;
    }

    /**
     * provides shooting into point on the board
     * return true if shooting is success 
     * @param board
     * @param point
     * @return true if shooting is success  
     */
    public static Boolean shoot(SBBoard board, SBPoint point) {
        SBPoint boardPoint = board.getPointArray()[point.getY()][point.getX()];
        boardPoint.setIsAlive(Boolean.FALSE);
        if (boardPoint.getIsWithShip() != null && boardPoint.getIsWithShip()) {
            if (boardPoint.getShip().isShipKilled()) {
                refreshShipAround(board, boardPoint.getShip());
            }
            return true;
        } else {
            return false;
        }
    }

    public static SBBoard randomShipLocation(SBBoard board) {
        randomShipLocation(board, 4, SBConstants.SB_FOUR_DECK_SHIP_COUNT);
        randomShipLocation(board, 3, SBConstants.SB_THREE_DECK_SHIP_COUNT);
        randomShipLocation(board, 2, SBConstants.SB_TWO_DECK_SHIP_COUNT);
        randomShipLocation(board, 1, SBConstants.SB_ONE_DECK_SHIP_COUNT);
        return board;
    }

    private static void randomShipLocation(SBBoard board, int size, int count) {
        for (int i = 0; i < count; i++) {
            boolean located = false;
            while (!located) {
                Ship ship = generateRandomShip(size);
                located = locateShip(board, ship, true);
            }
        }
    }

    private static Ship generateRandomShip(int size) {
        if (size > 4) {
            throw new IllegalArgumentException("Size should be less or equal 4");
        }
        int xHead = RandomUtils.generateInt(0, SBConstants.SB_BOARD_DIMENSION - size);
        int yHead = RandomUtils.generateInt(0, SBConstants.SB_BOARD_DIMENSION - size);
        boolean horizontal = Math.random() < 0.5 ? true : false;
        Integer[][] coordinates = new Integer[size][];
        for (int i = 0; i < size; i++) {
            if (horizontal) {
                coordinates[i] = new Integer[]{xHead + i, yHead};
            } else {
                coordinates[i] = new Integer[]{yHead, xHead + i};
            }
        }
        return buildShip(coordinates);
    }

    private static Ship buildShip(Integer points[][]) {
        List<SBPoint> pointList = new ArrayList<SBPoint>();
        for (Integer[] pointCoordinates : points) {
            if (pointCoordinates.length != 2) {
                throw new IllegalArgumentException("Array size should be equal 2");
            }
            SBPoint point = new SBPoint(Boolean.TRUE, Boolean.TRUE, pointCoordinates[0], pointCoordinates[1]);
            pointList.add(point);
        }
        return new Ship(pointList);
    }

    //проверяет не занята ли ета и соседние точки на поле
    private static boolean checkPointPossibility(SBBoard board, SBPoint point) {
        SBPoint[][] pointArray = board.getPointArray();
        int x = point.getX();
        int y = point.getY();
        for (int j = x - 1; j <= x + 1; j++) {
            for (int i = y - 1; i <= y + 1; i++) {
                if (!checkSimplePointPossibility(pointArray, j, i)) {
                    return false;
                }
            }
        }
        return true;
    }

    private static boolean checkSimplePointPossibility(SBPoint[][] pointArray, int x, int y) {
        if (y < 0 || y >= SBConstants.SB_BOARD_DIMENSION) {
            return true;
        }
        if (x < 0 || x >= SBConstants.SB_BOARD_DIMENSION) {
            return true;
        }
        SBPoint point = pointArray[y][x];
        return !point.getIsWithShip();
    }

    private static void locateShipOnTheBoard(SBBoard board, Ship ship) {

        List<SBPoint> pointList = new ArrayList<SBPoint>();
        for (SBPoint point : ship.getPointList()) {
            SBPoint newPoint = board.getPointArray()[point.getY()][point.getX()];
            newPoint.setIsWithShip(Boolean.TRUE);
            pointList.add(newPoint);
        }
        ship = new Ship();
        ship.addPoints(pointList);
        board.addShip(ship);
    }

    private static void refreshShipAround(SBBoard board, Ship ship) {
        SBPoint[][] pointArray = board.getPointArray();
        for (SBPoint point : ship.getPointList()) {
            int x = point.getX();
            int y = point.getY();
            for (int j = x - 1; j <= x + 1; j++) {
                for (int i = y - 1; i <= y + 1; i++) {
                    if (j >= 0 && j < SBConstants.SB_BOARD_DIMENSION
                            && i >= 0 && i < SBConstants.SB_BOARD_DIMENSION) {
                        pointArray[i][j].setIsAlive(Boolean.FALSE);

                    }
                }
            }
        }
    }
}
